package com.ld.common.webnotice.socket;

import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

import org.apache.commons.lang3.StringUtils;

import com.corundumstudio.socketio.AckCallback;
import com.corundumstudio.socketio.AckRequest;
import com.corundumstudio.socketio.SocketIOClient;
import com.corundumstudio.socketio.SocketIOServer;
import com.corundumstudio.socketio.listener.DataListener;
import com.ld.common.webnotice.model.PushMessage;
import com.ld.common.webnotice.model.SocketIOMsgType;
import com.ld.shieldsb.common.core.collections.ListUtils;
import com.ld.shieldsb.common.core.collections.MapUtils;

import lombok.extern.slf4j.Slf4j;

@Slf4j
public class SocketIOServiceImpl implements SocketIOService {

    // 用来存已连接的客户端
    private static Map<String, Map<String, SocketIOClient>> clientMap = new ConcurrentHashMap<>(); // 存储客户端信息,不同用户可能登录多个客户端，外面map的可以为userId，里面map的key为sessionId

    // 静态方法获取
    private SocketIOServer socketIOServer = SocketIOServerInstance.getSocketIOServer();

    /**
     * Spring IoC容器创建之后，在加载SocketIOServiceImpl Bean之后启动
     * 
     * @throws Exception
     */
    // @PostConstruct
//    private void autoStartup() throws Exception {
//        start();
//    }

    /**
     * Spring IoC容器在销毁SocketIOServiceImpl Bean之前关闭,避免重启项目服务端口占用问题
     * 
     * @throws Exception
     */
    // @PreDestroy
//    private void autoStop() throws Exception {
//        stop();
//    }

    @Override
    public void start() {
        // 监听客户端连接
        socketIOServer.addConnectListener(client -> {
            String sessionId = client.getSessionId().toString();
            log.warn(client.getRemoteAddress() + " web客户端接入" + sessionId);
            client.sendEvent(SocketIOMsgType.TYPE_PUBLIC_WELCOME, "hello");
            String userId = getParamsByClient(client);
            client.set("uid", userId); // 用户标识设置到client中，后面可以直接取用
            if (userId != null) {
                storeClient(userId, sessionId, client);
            }
        });

        // 监听客户端断开连接
        socketIOServer.addDisconnectListener(client -> {
            String sessionId = client.getSessionId().toString();
            log.warn(client.getRemoteAddress() + " web客户端断开连接" + sessionId);
//            String userId = getParamsByClient(client);
            String userId = client.get("uid");
            if (userId != null) {
                removeClient(userId, sessionId);
                client.disconnect();
            }
        });

        // 处理自定义的事件，与连接监听类似
        socketIOServer.addEventListener(PUSH_EVENT, PushMessage.class, (client, data, ackSender) -> {
            // TODO do something
        });

        // 握手请求
        socketIOServer.addEventListener("helloevent", PushMessage.class, new DataListener<PushMessage>() {
            @Override
            public void onData(final SocketIOClient client, PushMessage data, AckRequest ackRequest) {
                // 握手
                if (data.getMessage().equals("hello")) {
                    String userid = data.getFromUserId();
                    log.debug(Thread.currentThread().getName() + "web读取到的userid：" + userid);
                    if (userid != null) {

                        // send message back to client with ack callback WITH data
                        client.sendEvent("hellopush", new AckCallback<String>(String.class) {
                            @Override
                            public void onSuccess(String result) { // result为客户端的回传信息
                                log.warn("ack from client: " + client.getSessionId() + " data: " + result);
                            }
                        }, 30);
                    }

                } else {
                    log.info("行情接收到了不应该有的web客户端请求1111...");
                }
            }
        });

        socketIOServer.start();
    }

    @Override
    public void stop() {
        if (socketIOServer != null) {
            socketIOServer.stop();
            socketIOServer = null;
        }
    }

    /**
     * 发送给用户
     * 
     * @Title pushMessageToUser
     * @author 吕凯
     * @date 2019年7月10日 上午11:19:42
     * @param pushMessage
     * @see com.ld.common.webnotice.socket.SocketIOService#pushMessageToUser(com.ld.common.webnotice.model.PushMessage)
     */
    @Override
    public void pushMessageToUser(PushMessage pushMessage) {
        String userId = pushMessage.getToUserId();
        if (StringUtils.isNotBlank(userId)) {
            List<SocketIOClient> clients = getClient(userId);
            if (ListUtils.isNotEmpty(clients)) {
                clients.forEach(client -> client.sendEvent(SocketIOMsgType.TYPE_PRIVATE, pushMessage));
            }
        }
    }

    /**
     * 发送给具体的客户端
     * 
     * @Title pushMessageToUser
     * @author 吕凯
     * @date 2019年7月11日 下午4:20:27
     * @param client
     * @param pushMessage
     * @see com.ld.common.webnotice.socket.SocketIOService#pushMessageToUser(com.corundumstudio.socketio.SocketIOClient,
     *      com.ld.common.webnotice.model.PushMessage)
     */
    @Override
    public void pushMessageToUser(SocketIOClient client, PushMessage pushMessage) {
        client.sendEvent(SocketIOMsgType.TYPE_PRIVATE, new AckCallback<String>(String.class) {
            @Override
            public void onSuccess(String result) { // result为客户端的回传信息，该方法异步
                log.warn("客户端接收信息: " + client.getSessionId() + " data: " + result);
            }
        }, pushMessage);
    }

    /**
     * 发送个群组
     * 
     * @Title pushMessageToGroup
     * @author 吕凯
     * @date 2019年7月10日 上午11:19:54
     * @param pushMessage
     * @see com.ld.common.webnotice.socket.SocketIOService#pushMessageToGroup(com.ld.common.webnotice.model.PushMessage)
     */
    @Override
    public void pushMessageToGroup(PushMessage pushMessage) {
        // 暂不实现
    }

    /**
     * 发送给全体
     * 
     * @Title pushMessageToAll
     * @author 吕凯
     * @date 2019年7月10日 上午11:20:14
     * @param pushMessage
     * @see com.ld.common.webnotice.socket.SocketIOService#pushMessageToAll(com.ld.common.webnotice.model.PushMessage)
     */
    @Override
    public void pushMessageToAll(PushMessage pushMessage) {
        // 获取全部客户端
        Collection<SocketIOClient> allClients = socketIOServer.getAllClients();
        for (SocketIOClient socket : allClients) {
            socket.sendEvent(pushMessage.getType(), pushMessage);
        }

    }

    /**
     * 此方法为获取client连接中的参数，可根据需求更改
     * 
     * @param client
     * @return
     */
    private String getParamsByClient(SocketIOClient client) {
        // 从请求的连接中拿出参数（这里的uid必须是唯一标识）
//        Map<String, List<String>> params = client.getHandshakeData().getUrlParams();
//       List<String> list = params.get("uid");
//        if (list != null && list.size() > 0) {
//            return list.get(0);
//        } 
        String uid = client.getHandshakeData().getSingleUrlParam("uid");
        return uid;
    }

    private void storeClient(String uid, String sessionId, SocketIOClient client) {
        Map<String, SocketIOClient> userClientMap = clientMap.get(uid); // TODO 存在线程安全问题，待优化
        if (userClientMap == null) {
            userClientMap = new ConcurrentHashMap<>();
        }
        userClientMap.put(sessionId, client);
        clientMap.put(uid, userClientMap);
    }

    private void removeClient(String uid, String sessionId) {
        Map<String, SocketIOClient> userClientMap = clientMap.get(uid); // TODO 存在线程安全问题，待优化
        if (userClientMap != null) {
            userClientMap.remove(sessionId);
        }
    }

    private List<SocketIOClient> getClient(String uid) {
        Map<String, SocketIOClient> userClientMap = clientMap.get(uid); // TODO 存在线程安全问题，待优化
        if (userClientMap == null) {
            userClientMap = new ConcurrentHashMap<>();
        }
        return MapUtils.getValueList(userClientMap);
    }

    /**
     * 获取server
     * 
     * @Title getSocketIOServer
     * @author 吕凯
     * @date 2019年7月11日 上午11:41:32
     * @return SocketIOServer
     */
    public SocketIOServer getSocketIOServer() {
        return socketIOServer;
    }

    /**
     * 获取客户端信息
     * 
     * @Title getClientMap
     * @author 吕凯
     * @date 2019年7月11日 上午11:49:00
     * @return Map<String,Map<String,SocketIOClient>>
     */
    public static Map<String, Map<String, SocketIOClient>> getClientMap() {
        return clientMap;
    }

}